package gwtgaetools.server;

import java.util.ConcurrentModificationException;

import javax.jdo.JDODataStoreException;
import javax.jdo.JDOOptimisticVerificationException;
import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;

import com.google.apphosting.api.ApiProxy.CancelledException;

public abstract class TransactionTask {

	// if things worked or not.
	public boolean		success			= false;

	PersistenceManager	pm;

	public static int	DEFAULT_RETRIES	= 5;
	int					retries			= DEFAULT_RETRIES;

	public PersistenceManager pm() {
		return pm;
	}

	public TransactionTask(PersistenceManager p, boolean execute) throws Exception {

		pm = p;

		if (execute) run();
	}

	public TransactionTask(PersistenceManager p) throws Exception {
		this(p, true);
	}

	public void run() throws Exception {
		while (true) {

			Transaction txn = null;

			try {
				// PREPARE
				boolean keepGoing = initialize();
				// DO UPDATES
				if (keepGoing) {
					txn = pm.currentTransaction();
					txn.begin();
					success = doUpdates();
					txn.commit();
				}
				break;
			}

			catch (ConcurrentModificationException | JDOOptimisticVerificationException | JDODataStoreException | CancelledException e) {
				success = false;
				System.out.println("ServerRPC: retrying transaction... " + retries + " retries left");
				// e.printStackTrace();
				if (retries == 0) {
					System.out.println("ServerRPC: transaction failed");
					e.printStackTrace();
					if (txn.isActive()) {
						txn.rollback();
					}
					throw e;
				}
				--retries;
			}

			catch (Exception e) {
				success = false;
				// FAIL TRANSACTION
				System.out.println("ServerRPC (E): transaction failed");
				e.printStackTrace();
				if (txn.isActive()) {
					txn.rollback();
				}
				throw e;
			}

			finally {
				if (txn != null && txn.isActive()) {
					txn.rollback();
				}
				pm.flush();
			}
		}

		if (success) onSuccess();
		else onFail();
	}

	public boolean initialize() {
		return true;
	};

	public abstract boolean doUpdates();

	public void onFail() throws Exception {
		throw new Exception("generic error on server");
	};

	public void onSuccess() throws Exception {};

}
